﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WCAD
{
    public class 参数化
    {
        /// <summary>
        /// 修改参数化参数，没仔细研究，先放着备个份
        /// </summary>



        //Use:

        //Illustrates use of Associative API for manipulating parameters

        //Philippe Leefsma, Developer Technical Services. March 2012.

        [CommandMethod("AssocParameterCmd")]
        public void AssocParameterCmd()
        {

            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            PromptStringOptions pso = new PromptStringOptions("Enter Parameter Name: ");
            pso.AllowSpaces = false;
            PromptResult pr = ed.GetString(pso);
            if (pr.Status != PromptStatus.OK) return;
            ObjectId varId = GetVariableByName(db.CurrentSpaceId, pr.StringResult, false);
            if (varId != ObjectId.Null)
            {
                PromptKeywordOptions pko = new PromptKeywordOptions("\nThe Parameter already exist, select option:");
                pko.AllowNone = false;
                pko.Keywords.Add("Getvalue");
                pko.Keywords.Add("Rename");
                pko.Keywords.Add("Modify");
                pko.Keywords.Default = pko.Keywords[0].LocalName;
                PromptResult pkr = ed.GetKeywords(pko);
                if (pkr.Status != PromptStatus.OK) return;
                switch (pkr.StringResult)
                {
                    case "Getvalue":
                        string name = string.Empty;
                        ResultBuffer value = null;
                        string expression = string.Empty;
                        GetVariableValue(varId, ref name, ref value, ref expression);
                        ed.WriteMessage("\n" + name + " = " + expression + " (Evaluated: " + value.AsArray()[0].Value.ToString() + ")"); break;
                    case "Rename":
                        while (true)
                        {
                            try
                            {

                                PromptStringOptions psoRename = new PromptStringOptions("\nEnter parameter new name:");
                                PromptResult prRename = ed.GetString(psoRename);
                                if (prRename.Status != PromptStatus.OK) return;
                                RenameVariable(varId, prRename.StringResult, true);
                                break;
                            }
                            catch
                            {
                                ed.WriteMessage("\nInvalid name, please try again...");
                            }

                        }
                        break;
                    case "Modify":
                        while (true)
                        {
                            try
                            {

                                PromptStringOptions psoExpr = new PromptStringOptions("\nEnter parameter new expression:");
                                PromptResult prExpr = ed.GetString(psoExpr);
                                if (prExpr.Status != PromptStatus.OK) return;
                                SetVariableValue(varId, null, prExpr.StringResult);
                                break;
                            }
                            catch
                            {
                                ed.WriteMessage("\nInvalid expression, please try again...");
                            }
                        }
                        break;

                    default:
                        return;
                }
            }
            else
            {

                PromptKeywordOptions pko = new PromptKeywordOptions(

                 "\nThe Parameter doesn't exist, do you want to create it?");

                pko.AllowNone = false;

                pko.Keywords.Add("Yes");

                pko.Keywords.Add("No");

                pko.Keywords.Default = pko.Keywords[0].LocalName;
                PromptResult pkr = ed.GetKeywords(pko);
                if (pkr.Status != PromptStatus.OK)
                    return;
                if (pkr.StringResult == "No")
                    return;
                varId = GetVariableByName(db.CurrentSpaceId, pr.StringResult, true);
                ed.WriteMessage("\nParameter created [Id:" + varId.ToString() + "]");
            }
        }


        [CommandMethod("Ycsht")]
        public void 替换参数()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            //var curPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var curDwgFileName = doc.Name;
            var configTxtFileName = Path.ChangeExtension(curDwgFileName, ".txt");
            // Autodesk.AutoCAD.Windows.OpenFileDialog ofd = new Autodesk.AutoCAD.Windows.OpenFileDialog("打开配置文件", null, "txt;csv;*", "选择配置文件", OpenFileDialog.OpenFileDialogFlags.DefaultIsFolder|OpenFileDialog.OpenFileDialogFlags.ForceDefaultFolder| OpenFileDialog.OpenFileDialogFlags.NoUrls);
            if (File.Exists(configTxtFileName))
            {//if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                var configs = File.ReadAllLines(configTxtFileName, System.Text.Encoding.GetEncoding(0));
                int i = 0;
                foreach (var item in configs)
                {
                    var temps = item.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
                    if (temps.Length <= 1)
                    {
                        i++;
                        ed.WriteMessage($"\n配置文件中的第{i}行中无约定的分隔符\"=\",{item}");
                        continue;
                    }
                    ObjectId varId = GetVariableByName(db.CurrentSpaceId, temps.First(), false);
                    if (varId != ObjectId.Null)
                    {
                        try
                        {
                            string name = string.Empty;
                            ResultBuffer value = null;
                            string expression = string.Empty;
                            GetVariableValue(varId, ref name, ref value, ref expression);
                            ed.WriteMessage($"\n 参数 \"{temps.First()}\"的表达式从 {expression} 改为 {temps.Last()}");
                            SetVariableValue(varId, null, temps.Last());
                        }
                        catch
                        {
                            ed.WriteMessage($"\n 参数\"{temps.First()}\" 错误的表达式 {temps.Last()}，请重试...");
                        }
                    }
                    else
                        ed.WriteMessage($"\n 参数\"{temps.First()}\"不存在...");
                }
            }
            else
            {
                Application.ShowAlertDialog($"配置文件\"{configTxtFileName}\"不存在,请检查,配置文件必须与dwg文件同路径且文件名称相同！");
                OpenDir(curDwgFileName, true);
                return;
            }

        }
        ObjectId AddNewVariableToAssocNetwork(ObjectId networkId, string name, string expression)
        {
            using (Transaction Tx = networkId.Database.TransactionManager.StartTransaction())
            {
                using (AssocNetwork network = Tx.GetObject(networkId, OpenMode.ForWrite, false) as AssocNetwork)

                {

                    AssocVariable var = new AssocVariable();
                    network.Database.AddDBObject(var);
                    network.AddAction(var.ObjectId, true);
                    string errMsgValidate = string.Empty;
                    var.ValidateNameAndExpression(name, expression, ref errMsgValidate);
                    var.SetName(name, true);
                    var.Value = new ResultBuffer(new TypedValue[] { new TypedValue((int)DxfCode.Real, 1.0) });
                    string errMsgEval = string.Empty;
                    var.SetExpression(expression, "", true, true, ref errMsgEval, false);
                    ResultBuffer evaluatedExpressionValue = new ResultBuffer();
                    var.EvaluateExpression(ref evaluatedExpressionValue);
                    var.Value = evaluatedExpressionValue;
                    Tx.AddNewlyCreatedDBObject(var, true);
                    Tx.Commit();
                    return var.ObjectId;
                }
            }
        }

        ObjectId GetVariableByName(ObjectId btrId, string name, bool createIfDoesNotExist)
        {
            if (name.Length == 0) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput);
            ObjectId networkId = AssocNetwork.GetInstanceFromObject(btrId, createIfDoesNotExist, true, "ACAD_ASSOCNETWORK");
            if (networkId == ObjectId.Null) return ObjectId.Null;
            using (Transaction Tx = btrId.Database.TransactionManager.StartTransaction())
            {
                using (AssocNetwork network = Tx.GetObject(networkId, OpenMode.ForRead, false) as AssocNetwork)
                {
                    foreach (ObjectId actionId in network.GetActions)
                    {
                        if (actionId == ObjectId.Null) continue;
                        if (actionId.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(AssocVariable))))
                        {
                            //Check if we found our guy
                            AssocVariable var = Tx.GetObject(actionId, OpenMode.ForRead, false) as AssocVariable;
                            if (var.Name == name)
                                return actionId;
                        }
                    }
                }
            }
            //If we don't want to create a new variable, returns an error
            if (!createIfDoesNotExist)
                return ObjectId.Null;
            return AddNewVariableToAssocNetwork(networkId, name, "1.0");
        }

        void RenameVariable(ObjectId variableId, string newName, bool updateReferencingExpressions)
        {
            using (Transaction Tx = variableId.Database.TransactionManager.StartTransaction())
            {
                AssocVariable var = Tx.GetObject(variableId, OpenMode.ForRead) as AssocVariable;
                string errMsgValidate = string.Empty;
                var.ValidateNameAndExpression(newName, var.Expression, ref errMsgValidate);
                var.UpgradeOpen();
                var.SetName(newName, updateReferencingExpressions);
                Tx.Commit();
            }
        }

        void GetVariableValue(ObjectId variableId, ref string name, ref ResultBuffer value, ref string expression)
        {
            using (Transaction Tx = variableId.Database.TransactionManager.StartTransaction())
            {
                AssocVariable var = Tx.GetObject(variableId, OpenMode.ForRead) as AssocVariable;
                name = var.Name;
                var.EvaluateExpression(ref value);
                expression = var.Expression;
                Tx.Commit();
            }
        }

        void SetVariableValue(ObjectId variableId, ResultBuffer value, string expression)
        {

            using (Transaction Tx = variableId.Database.TransactionManager.StartTransaction())
            {
                AssocVariable var = Tx.GetObject(variableId, OpenMode.ForWrite) as AssocVariable;
                if (expression != string.Empty)
                {
                    string errMsg = string.Empty;
                    var.ValidateNameAndExpression(var.Name, expression, ref errMsg);
                    var.SetExpression(expression, "", true, true, ref errMsg, true);
                    ResultBuffer evaluatedExpressionValue = null;
                    var.EvaluateExpression(ref evaluatedExpressionValue);
                    var.Value = evaluatedExpressionValue;
                }
                else var.Value = value;
                Tx.Commit();
            }
        }

        /// <summary>
        /// 打开文件所在的文件夹
        /// </summary>
        /// <param name="fn">文件名称</param>
        /// <param name="select">是否选中文件</param>
        void OpenDir(string fn, bool select = false)
        {
            //
            string windir = Environment.GetEnvironmentVariable("windir");
            if (string.IsNullOrEmpty(windir.Trim())) windir = @"C:\Windows\";
            if (!windir.EndsWith("\\")) windir += "\\";
            var pi = new ProcessStartInfo($"{windir}explorer.exe")
            {
                Arguments = $"{(select ? @"/select," : "")}\"{fn}\"",
                WindowStyle = ProcessWindowStyle.Normal,
                WorkingDirectory = windir
            };
            Process.Start(pi);
        }
    }
}
